[Chapter 32] Class Macros
Author: Bryan Costales & Eric Allman
Date: 1999-01-06T20:20:24Z

#

| Chapter 32 |

# 32. Class Macros

Contents:
Class Configuration Commands
Access Class in Rules
Class with m4
Pitfalls
Alphabetized Reference

A class is like an array of string values. In the LHS of rules it is sometimes advantageous to compare individual tokens to multiple strings when determining a match. The configuration class command provides this ability. The class command is similar to the macro definition command, except that instead of assigning a single value to a macro, it assigns many values to a class. Classes differs from macros in that they can be used only in the LHS of rules, whereas macros can be used in either the RHS or the LHS.

Two different configuration commands can be used to assign values to a class. The `C` configuration command is used to assign values from within the configuration file. The `F` configuration command is used in two ways: to assign values by reading them from a disk file or to assign values by running a program and reading the output. These commands may be intermixed to create a single class or used separately to create multiple classes.

You may wish to review the tutorial in Chapter 12, Class, to see a few typical applications of class.

## 32.1 Class Configuration Commands

The three forms for the class configuration command are the following:

>     CX list            values from configuration file
>     FX file            values from a disk file
>     FX |program        values via another program

The class configuration command starts with either the letter `C` or `F`, which must begin a line. The `C` says values will be assigned as a part of the configuration command. The `F` says values will be assigned from an external file or program.

The `C` or `F` is immediately followed (with no intervening whitespace) by the name of the class (the `X` above). A class name is any single ASCII character or, beginning with V8.7 sendmail a multicharacter name enclosed in curly braces:

>     CX list                all versions
>     C{LongName} list       beginning with V8.7

See Section 31.4.2, "Multicharacter Names" for a full discussion of how to use multicharacter names.

Note that classes are separate from macros, so they may both use the same letter or name with no conflict.

The sendmail program reserves the lowercase letters for its own use as internally defined class names (although currently only a few are actually used). All uppercase letters and all names that begin with uppercase letters are available for use.

### 32.1.1 The C Class Command

The `C` form of the class command causes values to be assigned from within the configuration file. In general, the class command looks like this:

>     CX list               values from configuration file
>     C{XX} list            values from configuration file

Here, a `list` is a list of string elements (delimited by whitespace) that follows on the same line as the `C` command. Each word in `list` is appended to the array of values in the class `X` in the first case and to the class `{XX}` in the second. [1]

> [1] Note that when a class name is a single character, it may be referenced with or without enclosing curly braces, with no change in meaning. That is, `CX` and `C{X}` are equivalent.

Multiple declarations of the same named class may coexist in the configuration file. Each declaration after the first appends its string elements to the preceding list. That is,

>     CX string1 string2
>     CX string3 string4

produces the same class as does

>     CX string1 string2 string3 string4

Both create a class with four strings as elements.

When an array of values is built, whitespace is used to separate one value from another. Whitespace is defined by the C language isspace(3) routine and usually includes the space, tab, newline, carriage-return, and form-feed characters. Each line of text assigned to a class is broken up by sendmail into whitespace delimited words. When a line is indented with a space or a tab, that line is joined by sendmail to the preceding line. Thus the following three declarations also append four words to the class `X`:

>     CX string1
>     CX string2
>     CX string3
>           string4

>        tab

Words that are added to a class cannot be removed after sendmail has read them. Instead, they must be edited out of whatever file or program produced them, and the sendmail daemon must be killed and restarted.

The list of words in a class declaration can include macros. For example the following assigns the same values to class `X` as did the above example:

>     D{LIST} string1 string2 string3 string4
>     CX ${LIST}

Macros used in class declarations are expanded when the configuration file is read. Deferred macros (those with the `$&` prefix) may not be used in class declarations. But conditionals may:

>     CX ourhost$?{Domain}.${Domain}$.

### 32.1.2 The F Class Command

The `F` form of the class configuration command allows values to be appended to a class from outside the configuration file. In general, the file command looks like either of the following:

>     FX file            values from a disk file
>     FX |program        values via another program (V8.7 and above, or IDA)

The `F` is immediately followed by the name of the class. This can be either a single-character name, as shown, or a multicharacter name. The name is followed by optional whitespace and then the name of a file or program. If the file or program begins with the pipe character (`|`), it is taken to be the name of a program to run. [2] Otherwise, it is taken to be the name of a file to read.

> [2] This was removed from V8.1 sendmail because it presented a security risk. It was restored to V8.7 because sendmail now checks permissions more carefully and exec(2)'s the program itself instead of using the old, buggy popen(3) approach of yore.

If SCANF was defined when sendmail was compiled (see Section 18.8.39, SCANF), each line that is read from a file or program is parsed by the C language scanf(3) library routine. The formatting pattern given to scanf(3) is `%s`, which tells scanf(3) to read only the first whitespace-delimited word from each line of text. The file is opened for reading or the program is executed when the configuration file is processed. If either cannot be opened (for reading or execution), sendmail syslog(3)'s the following error and ignores that configuration command:

>     cannot open what: why        file
>     cannot exec what: why        program

Here, the what is the exact text that was given in the configuration file, and `why` is the text of a system error.

For the file form only, if the file may optionally not exist, you can prefix its name with a `-o` switch:

>     FX -o file            okay for file to not exist

This tells sendmail to remain silent if the file does not exit. The `-o` switch is useful when a configuration file is shared by several machines, only some of which need the external class macro file.

The `C` and `F` forms of the configuration command may be intermixed for any given class name. For example, consider a file named /etc/local/names with the following contents:

>     string3
>     string4

The following two configuration commands add the same four strings to the class `X` as did the `C` command alone in the previous section:

>     CX string1 string2
>     FX /etc/local/names

This creates a class with four strings as array elements. Whitespace delimits one string from the others in the `C` line declaration, and they are stored in the order in which they are listed. The file /etc/local/names is then opened and read, and each of the two words in that file is appended to the two words that are already in the class.

#### 32.1.2.1 scanf(3) variations

The file form of the class configuration command allows different formatting patterns to be used with scanf(3). [3] But the program form does not allow any variation, and so its scanf(3) pattern is always `%s`, which tells scanf(3) to read only the first whitespace-delimited word from each line of text:

> [3] The version of sendmail that you are using must have been compiled with SCANF defined (see Section 18.8.39) for scanf(3) to be usable from within the configuration file.

>     FX file pat        with scanf(3) pattern
>     FX |program        always ``%s''

If the optional `pat` argument to the file form is missing, the pattern given to scanf(3) is `%s`. The `pat` argument is separated from the `file` argument by one or more spaces or tabs. It should not be quoted, and it consists of everything from its first character to the end of the line. Internally, scanf(3) is called with:

>     sscanf(result, pat, input)

Here, `result` is the string array element to be added to the class definition. The `pat` is the scanf(3) pattern, and `input` is the line of text read from the file. [4]

> [4] Avoid using a bare `%s` as the pattern. Doing so risks overflowing internal sendmail buffers. Instead specify a limited string length with something like `%.40s`.

After each line of text is read from the file and filtered with the scanf(3) pattern, it is further subdivided by sendmail into individual words. That subdividing uses whitespace (as defined by the C language isspace(3) routine) to separate words. Each separate word is then appended as an individual element to the class array.

Consider the contents of the following file named /etc/local/myhosts:

>     server1 server2 # my two nets
>     uuhost          # my uucp alias
>     #mailhost       # mail server alias (retired 06,23,91)

This file contains three hostname aliases to be added to a class, say `H`. The following configuration command does just that:

>     FH /etc/local/myhosts %[^\#]

The pattern `%[^#]` causes scanf(3) to read all characters in each line up to, but not including, the first `#` character. The first line includes two white-space delimited words that are appended to the class `H`. The second line contains one word, and the third contains none. The `\` character prevents sendmail from treating the `#` as a comment character.

31.10 Alphabetized Reference |  | 32.2 Access Class in Rules